﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DFASimulation
{
    public class DFAInterface
    {
        private DFASystem dfasystem;
        public DFAState finalState;
        public DFAState startState;
        private List<DFAState> Q;
        private List<DFAState> F;
        private List<char> rules;
        private List<List<DFAState>> transitions;
        private int num_of_states;

        public ConsoleColor backcolor = ConsoleColor.White;
        public ConsoleColor forecolor = ConsoleColor.Blue;
        public String title ="|----------- WELCOME TO DFA SIMULATION -----------|";
        public String welcome_msg = "Welcome to DFA Simulation Demo CLI";
        private String egk696 = " \t\t\t\t(DLL created By Lefteris Kyriakakis @2013)";

        private void setStates()
        {
            dfasystem.setStates(Q);
        }

        private void setRules()
        {
            dfasystem.createRules(rules);
        }

        private void setTransitions()
        {
            dfasystem.createTransitions(transitions);
        }

        private void initializeCLI()
        {
            Console.Title = title + egk696;
            Console.BackgroundColor = backcolor;
            Console.ForegroundColor = forecolor;
            Console.Clear();
            Console.WriteLine(welcome_msg + "\n");
        }

        public DFAInterface(Boolean CLI)
        {
            dfasystem = new DFASystem();
            if(CLI)
                initializeCLI();
        }

        public DFAInterface(List<DFAState> Q, List<DFAState> F)
        {
            initializeCLI();
            this.Q = Q;
            dfasystem = new DFASystem(Q);
            this.F = F;
        }

        public DFAInterface(List<DFAState> Q, List<DFAState> F, List<char> rules, List<List<DFAState>> transitions)
        {
            initializeCLI();
            this.Q = Q;
            dfasystem = new DFASystem(Q);
            this.F = F;
            this.rules = rules;
            this.transitions = transitions;
            num_of_states = Q.Count;
            setRules();
            setTransitions();
        }

        public DFAInterface(int num_of_states, int start)
        {
            initializeCLI();
            this.Q = new List<DFAState>(num_of_states);
   
            for (int i = 0; i < num_of_states; i++)
            {
                Console.WriteLine("State " + i + " info: --------------------------");
                Console.Write("Enter an id for the state: ");
                Q.Add(new DFAState());
                Q.ElementAt(i).id = int.Parse(Console.ReadLine());
                Console.Write("Enter a description for the state: ");
                Q.ElementAt(i).desc = Console.ReadLine();
            }
            dfasystem = new DFASystem(Q);
            setStartState(start);
        }

        public DFAInterface(int num_of_states, int start, List<char> rules, List<List<DFAState>> transitions)
        {
            initializeCLI();
            this.Q = new List<DFAState>(num_of_states);

            for (int i = 0; i < num_of_states; i++)
            {
                Console.WriteLine("State " + i + " info: --------------------------");
                Console.Write("Enter an id for the state: ");
                Q.Add(new DFAState());
                Q.ElementAt(i).id = int.Parse(Console.ReadLine());
                Console.Write("Enter a description for the state: ");
                Q.ElementAt(i).desc = Console.ReadLine();
            }
            dfasystem = new DFASystem(Q);
            setStartState(start);

            this.rules = rules;
            setRules();
            this.transitions = transitions;
            setRules();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="start"></param>
        public void setStartState(int start)
        {
            foreach (var state in dfasystem.Q)
            {
                if (state.id == start)
                {
                    this.startState = state;
                    break;
                }
            }
        }

        /// <summary>
        /// A Commandline version of the setStartState(). Provides commandline internface
        /// to read the id and set the starting state.
        /// (Warning! It clears the console after its complete)
        /// </summary>
        public void setStartStateCLI()
        {
            Console.Write("Please enter the Id of the starting state in your graph: ");
            setStartState(int.Parse(Console.ReadLine()));
            Console.Clear();
            Console.WriteLine(welcome_msg + "\n");
        }

        public Boolean createAcceptStates(List<String> stateIds)
        {
            if (stateIds == null || stateIds.Count == 0)
                return false;
            if (stateIds.Count() > 0)
                F = new List<DFAState>(stateIds.Count);
            for (int i = 0; i < F.Capacity; i++)
            {
                int dummyStateId = int.Parse(stateIds[i]);
                var dummyState = (from s in Q where s.id == dummyStateId select s).FirstOrDefault();
                this.F.Add((DFAState)dummyState);
            }
            return true;
        }

        public List<DFAState> getAcceptStates()
        {
            return F;
        }

        /// <summary>
        /// It creates a new Q states set based on the input pairs of (stateID:stateName)
        /// </summary>
        /// <param name="stateNames">A list of the input state's Descriptions</param>
        /// <param name="stateIds">A list of the input state's Ids</param>
        /// <returns>If the creation of the Q is correct return true, else
        /// if the lists are null or empty or have different sizes it returns false.</returns>
        public Boolean createStates(List<String> stateNames, List<String> stateIds)
        {
            if (stateNames == null || stateNames.Count == 0
                && stateIds == null || stateIds.Count == 0)
                return false;
            if (stateIds.Count != stateNames.Count)
                return false;
            if (stateNames.Count() > 0)
                Q = new List<DFAState>(stateNames.Count);
            for (int i = 0; i < Q.Capacity; i++)
            {
                int id = i;
                String desc = stateNames[i];
                if (stateIds != null || stateIds.Count > 0)
                    id = int.Parse(stateIds[i]);
                Q.Add(new DFAState(id, desc));
            }
            setStates();
            return true;  
        }

        /// <summary>
        /// A Command Line version of the createStates() which contains a commandline
        /// wizard like guide to create the states.
        /// (Warning! It clears the console after its complete)
        /// </summary>
        public void createStatesCLI()
        {
            Console.Write("How many states has your graph ? ");
            this.Q = new List<DFAState>(int.Parse(Console.ReadLine()));
            this.num_of_states = Q.Capacity;
            for (int i = 0; i < num_of_states; i++)
            {
                Console.WriteLine("State " + i + " info: --------------------------");
                Console.Write("Enter an id for the state: ");
                Q.Add(new DFAState());
                Q.ElementAt(i).id = int.Parse(Console.ReadLine());
                Console.Write("Enter a description for the state: ");
                Q.ElementAt(i).desc = Console.ReadLine();
            }
            dfasystem.setStates(Q);
            Console.Clear();
            Console.WriteLine(welcome_msg + "\n");
        }

        public Boolean createRules(List<String> S)
        {
            if (S.Count == 0 || S.Count == null)
                return false;
            rules = new List<char>(S.Count);
            for (int i = 0; i < S.Count; i++)
            {
                rules.Add(char.Parse(S[i]));
            }
            setRules();
            return true;
        }

        /// <summary>
        /// A Command Line version of the createRules() which contains a commandline
        /// wizard like guid to create the rules.
        /// (Warning! It clears the console after its complete)
        /// </summary>
        public void createRulesCLI()
        {
            Console.Write("What is the count of set of symbols your system has ? ");
            this.rules = new List<char>(int.Parse(Console.ReadLine()));

            for (int i = 0; i < rules.Capacity; i++)
            {
                Console.Write("Enter symbol: ");
                rules.Add(char.Parse(Console.ReadLine()));
            }

            setRules();
            Console.Clear();
            Console.WriteLine(welcome_msg + "\n");
        }

        /// <summary>
        /// Creates the transitions for the set of rules. For each element in the set of rules
        /// it creates a transition to the id given in the list.
        /// </summary>
        /// <param name="transitions">Expects a double list of strings which represent the transitions</param>
        /// <example>Of the list expected for a set of {'0','1'} rules the transitions will be set as
        /// δ(Q[i],0)=transitions[i][j] and δ(Q[i],1)=transitions[i][j].
        /// Generally it does: δ(Q[i],rules[j])=transitions[i][j].</example>
        /// <returns>If the transitions where succesfully set then it return true, else if the given list is empty or null return false.</returns>
        public Boolean createTransitions(List<List<String>> transitions)
        {
            if (transitions.Count == 0 || transitions == null)
                return false;
            this.transitions = new List<List<DFAState>>(transitions.Count);
            for (int i = 0; i < transitions.Count; i++)
            {
                this.transitions.Add(new List<DFAState>(rules.Capacity));
                for (int j = 0; j < transitions[i].Count; j++)
                {
                    int dummyStateId = int.Parse(transitions[i][j]);
                    if (dummyStateId != -1)
                    {
                        var dummyState = (from s in Q where s.id == dummyStateId select s).FirstOrDefault();
                        this.transitions[i].Add((DFAState)dummyState);
                    }
                    else
                    {
                        this.transitions[i].Add(dfasystem.dummy);
                    }
                }
            }
            setTransitions();
            return true;
        }

        /// <summary>
        /// A Command Line version of the createTransitions() which contains a commandline
        /// wizard like guide to create the transitions.
        /// (Warning! It clears the console after its complete)
        /// </summary>
        public void createTransitionsCLI()
        {
            this.transitions = new List<List<DFAState>>(num_of_states);
            for (int i = 0; i < transitions.Capacity; i++)
            {
                
                transitions.Add(new List<DFAState>(rules.Capacity));
                for (int j = 0; j < transitions[i].Capacity; j++)
                {
                    Console.Write("For the transition δ({0},{1}) the next state Id is: ", Q[i].id, rules[j]);
                    int dummyStateId = int.Parse(Console.ReadLine());
                    var dummyState = (from s in Q where s.id == dummyStateId select s).FirstOrDefault();
                    transitions[i].Add((DFAState)dummyState);
                }
            }
            setTransitions();
            Console.Clear();
            Console.WriteLine(welcome_msg+"\n");
        }

        /// <summary>
        /// Displays all the elements of the created DFA system.
        /// </summary>
        public void viewSystemCLI()
        {
            Console.WriteLine("DFA System View: ");
            Console.WriteLine("The Set of states is Q=");
            Console.WriteLine(viewStates());
            Console.WriteLine();
            Console.WriteLine("The Set of Alphabet is Σ=");
            Console.WriteLine(viewRules());
            Console.WriteLine();
            Console.WriteLine("The Set of transitions is T");
            Console.WriteLine(viewTransitions());
            Console.WriteLine();
            Console.WriteLine("Starting State: S0 = {0}", viewStartState());
            Console.WriteLine();
        }

        /// <summary>
        /// View the Starting State.
        /// </summary>
        /// <returns>A String containing the starting state's id and description</returns>
        private String viewStartState()
        {
            return "("+startState.id+":"+startState.desc+")";
        }

        /// <summary>
        /// View the set of transitions (Transition Table) of the created DFA.
        /// </summary>
        /// <returns>A String containing a view of the transition table of the DFA</returns>
        public String viewTransitions()
        {
            String viewtransitions = "--------------------\n";
            for(int i=0;i<transitions.Capacity;i++)
            {
                viewtransitions += Q[i].desc + " ";
                for(int j=0;j<transitions[i].Capacity;j++)
                {
                    viewtransitions += "for " + rules[j] + "->" + Q[i].next[j].desc + " || ";
                }
                viewtransitions += "\n";
            }
            viewtransitions = viewtransitions.TrimEnd("\n".ToCharArray());
            viewtransitions += "\n--------------------";
            return viewtransitions;
        }

        /// <summary>
        /// View the set of rules (Language set) of the created DFA.
        /// </summary>
        /// <returns>A String containing a view of the set of states</returns>
        public String viewRules()
        {
            String viewrules = "{ ";
            foreach (var rule in rules)
            {
                viewrules += "(" + rule + "), ";
            }
            viewrules = viewrules.TrimEnd(new char[]{','});
            viewrules += " }";
            return viewrules;
        }
        /// <summary>
        /// View the set of states of the created DFA.
        /// </summary>
        /// <returns>A String containing a view of the set of states</returns>
        public String viewStates()
        {
            String viewstates = "{\n";
            foreach (var state in Q)
            {
                viewstates += "(" + state.id + ":" + state.desc + "), ";
            }
            viewstates = viewstates.TrimEnd(new char[] { ',' });
            viewstates += "\n}";
            return viewstates;
        }

        /// <summary>
        /// This functions calculates δ^(q,W) of the created DFA.
        /// W awaits a string to be passed as an argument, which the one
        /// that should be parsed and validated.
        /// </summary>
        /// <param name="W">Is the string to be parsed by the DFA</param>
        public void D(String W)
        {
            finalState = dfasystem.d(startState, W.ToCharArray(), 0);
        }

        public DFAState DOf(String W, DFAState startState)
        {
            return dfasystem.d(startState, W.ToCharArray(), 0);
        }

        public Boolean validateSimulation()
        {
            foreach (DFAState state in F)
            {
                if (state.id == finalState.id)
                {
                    return true;
                }
                else
                {
                    continue;
                }
            }
            return false;
        }
        
        /// <summary>
        /// This function is the same as the D(). It calculates the δ^(q,W).
        /// Where W is asked to be read from the console and q is the start state.
        /// This function uses recurtion to calculate the final state and prints out the state
        /// and ask if you want to enter another line.
        /// </summary>
        public void DCLI()
        {
            String calc = "Yes";
            while(calc.StartsWith("Y", StringComparison.CurrentCultureIgnoreCase)){
                Console.WriteLine("-------------------- PARSING ------------------");
                Console.Write("Enter your line string to be parsed by the DFA: ");
                D(Console.ReadLine());
                if (finalState.id == -1)
                    Console.WriteLine("Warning: Final state is a dummy state. The system rules and transitions where not defined correctly!");
                else
                    Console.WriteLine("Final State is :" + finalState.desc + " with ID: " + finalState.id);
                Console.WriteLine("Do you want to enter another line to be parsed? (Y/N): ");
                calc = Console.ReadLine();
            }
        }

        public List<DFAState> getStates()
        {
            return Q;
        }

        public List<char> getRules()
        {
            return rules;
        }

        public List<List<DFAState>> getTransitions()
        {
            return transitions;
        }

    }
}
